package com.gooseeker.mbus;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;

import org.apache.log4j.Logger;

import com.gooseeker.modbus.ByteUitls;
import com.gooseeker.modbus.CRC16Utils;
import com.gooseeker.modbus.ConstValue;
import com.gooseeker.util.Constants;

import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;

public class SerialPortEventListenerImpl implements SerialPortEventListener {
	private Logger logger = Logger.getLogger(SerialPortEventListenerImpl.class);
	
	private BufferedInputStream bis = null;
	public SerialPortEventListenerImpl(InputStream is)
	{
		bis = new BufferedInputStream(is);
	}
	@Override
	public void serialEvent(SerialPortEvent ev) {
		try 
		{
			Thread.sleep(Constants.CMD_RESEND_PERIOD);
		} catch (InterruptedException e) {
		}
		
		// 有数据可读
		// TODO 数据解析要单独出来，以流模式接收数据，根据地址和命令字及校验字段，解析出整帧数据
		// 如果发现当前帧数据不合法则跳过，后续这块要放到线程中单独解析
		// 当前的方案是收到数据就通过延时方法认为接收到的就是一帧完整的数据报文
		// 这样是存在漏洞的，而且解析是线程来完成，也就是说收到一帧起一个线程
		// TODO 应改为数据解析就一个线程，这具Listener只负责接收数据，把数据放到缓冲区内，而
		// 解析线程则不断从中取完整帧进行解析
		switch(ev.getEventType())
		{
		case SerialPortEvent.DATA_AVAILABLE: // 1
			try 
			{
				byte[] buffer = new byte[Constants.DEFAULT_RECV_BUFFER_SIZE];
				//每次取21字节
				while(bis.available() >= Constants.DEFAULT_RECV_BUFFER_SIZE)
				{
					bis.mark(0);
					int numBytes = bis.read(buffer);
					boolean isLagel = checkFrame(buffer,numBytes);
					if(isLagel)
					{
						//校验成功，则解析入库.解析线程默认10个，解析线程耗光则阻塞等等
						Constants.PARSE_THREAD_POOL.execute(new ParseFrameThread(buffer));
						logger.debug("the data " +ByteUitls.printByteArray(buffer, buffer.length) + " will parse .");
					}
					else
					{
						//校验失败则往后一个字节继续获取
						logger.info("check data frame fail . skip 1 and continue .");
						bis.reset();
						bis.skip(1);
					}
				}

			} catch (IOException e) {
				logger.error(" io error . "+e.getMessage());
			}
			break;
		default:
			break;
		}
		
	}
	
	private boolean checkFrame(byte[] buffer,int numBytes)
	{
		//长度校验
		if(buffer.length != Constants.DEFAULT_RECV_BUFFER_SIZE)
		{
			return false;
		}
		
		int addrInt = ByteUitls.byte2Int(buffer[0]);
		// 地址校验
		if (addrInt > ConstValue.SLAVE_MAX_ADDR || addrInt < ConstValue.SLAVE_MIN_ADDR)
		{
			logger.error("address is illagel and discard . the range should be [1~247]. this is " + addrInt);
			return false;
		}
		//crc校验
		byte[] crc = new byte[2];
		System.arraycopy(buffer, numBytes-2, crc, 0,2);
		return Arrays.equals(crc, CRC16Utils.getCrc16(buffer, numBytes - 2));
	}

}
